#pragma once

#include "fxcc/imgui/pch.h"

namespace fxcc
{
    namespace imgui
    {

        struct FXCC_API Editor
        {
            ImVec2 m_ImageSize;

            Editor() : m_ImageSize({50, 50}) {};

            struct FXCC_API Funcs
            {
                static int MyResizeCallback(ImGuiInputTextCallbackData *data)
                {
                    if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
                    {
                        ImVector<char> *my_str = (ImVector<char> *)data->UserData;
                        IM_ASSERT(my_str->begin() == data->Buf);

                        my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1
                        data->Buf = my_str->begin();
                    }
                    return 0;
                }

                // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.
                // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'
                static bool MyInputTextMultiline(const char *label, ImVector<char> *my_str, const ImVec2 &size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0)
                {
                    IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
                    return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size,
                                                     flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void *)my_str);
                }
            };

            bool InputMultiString(const std::string &label, std::string &value, int row);
            bool InputMultiString(const std::string label, ImVector<char> &my_str, int row);
            bool InputString(const std::string label, std::string &value);

            bool Checkbox(const std::string &label, int &open, int index);
            bool TreeNode(const std::string& label, int& open, int index, ImGuiTreeNodeFlags flag = 0);
            bool TreeNode(const std::string& label, bool& open, ImGuiTreeNodeFlags flag = 0);

            void TreePop()
            {
                ImGui::TreePop();
            }
            template <typename T>
            bool Edit(const std::string &label, T &t);

            template <typename T>
            bool NodeEdit(const std::string& label, T& t);

            template <typename T>
            bool Drag(const std::string &label, T &t);

            template <typename T>
            bool Color(const std::string &label, T &t);

            template <typename T>
            bool ColorInst(const std::string &label, T &t)
            {
                ImGui::PushID(&t);
                bool res = Color<T>(label, t);
                ImGui::PopID();
                return res;
            }

            template <typename T>
            bool NodeEditInst(const std::string& label, T& t)
            {
                ImGui::PushID(&t);
                bool res = NodeEdit<T>(label, t);
                ImGui::PopID();
                return res;
            }

            template <typename T>
            bool EditMapInst(const std::string& label, std::map<std::string, T>& ts)
            {
                bool flag{ 0 };

                for (auto& [key, value] : ts)
                {
                    flag |= EditInst<T>(key, value);
                }

                return flag;
            }

            template <typename T>
            bool ColorMapInst(const std::string& label, std::map<std::string, T>& ts)
            {
                bool flag{ 0 };

                ImGui::Text("%s", label.c_str());
                for (auto& [key, value] : ts)
                {
                    flag |= ColorInst<T>(key, value);
                }

                return flag;
            }


            template <typename T>
            bool TextShow(const std::string &label, T &t);

            template <typename T>
            bool TextShowInst(const std::string &label, T &t)
            {
                ImGui::PushID(&t);
                bool res = TextShow<T>(label, t);
                ImGui::PopID();
                return res;
            }
            template <typename T>
            bool Input(const std::string &label, T &t);

            template <typename T>
            bool PathEdit(const std::string& label, T& t);

            template <typename T>
            bool InputInst(const std::string &label, T &t)
            {
                ImGui::PushID(&t);
                bool res = Input<T>(label, t);
                ImGui::PopID();
                return res;
            }

            template <typename T>
            bool PathEditInst(const std::string& label, T& t)
            {
                ImGui::PushID(&t);
                bool res = PathEdit<T>(label, t);
                ImGui::PopID();
                return res;
            }
            template <typename T>
            bool ImageEdit(const std::string &label, T &t, const ImVec2 &sz = ImVec2(50, 50));

            template <typename T>
            bool ImageEditInst(const std::string &label, T &t, const ImVec2 &sz = ImVec2(50, 50))
            {
                ImGui::PushID(&t);
                bool res = ImageEdit<T>(label, t, sz);
                ImGui::PopID();
                return res;
            }
            template <typename T>
            bool TreeEdit(const std::string &label, T &t);

            template <typename T>
            bool TreeEditInst(const std::string &label, T &t)
            {
                ImGui::PushID(&t);
                bool res = TreeEdit<T>(label, t);
                ImGui::PopID();
                return res;
            }

            template <typename T>
            bool EditInst(const std::string &label, T &t)
            {
                ImGui::PushID(&t);
                bool res = Edit<T>(label, t);
                ImGui::PopID();
                return res;
            }

            template <typename T>
            bool EditInstList(const std::string& label, std::vector<T>& ts)
            {
                bool res = false;

                int index = 0;

                for (auto& t : ts)
                {
                    ImGui::PushID(&t);
                    res |= Edit<T>(t.m_Name, t);
                    ImGui::PopID();
                    index++;
                }
   
                return res;
            }

            template <typename T>
            bool DragInst(const std::string &label, T &t)
            {
                ImGui::PushID(&t);
                bool res = Drag<T>(label, t);
                ImGui::PopID();
                return res;
            }

            template <typename EnumType, typename DescType>
            bool Combo(const std::string &label, const std::vector<DescType> &descTypes, EnumType &inst, int startIndex = 1, int endIndex = 0)
            {
                const auto &desc = descTypes[(int)inst];

                bool beginCombo = ImGui::BeginCombo(label.c_str(), desc.m_Name.c_str());

                bool selectable = false;

                if (beginCombo)
                {
                    int numDescs = descTypes.size();

                    for (int i = startIndex; i < numDescs + endIndex; i++)
                    {
                        const auto &it = descTypes[i];
                        ImGui::PushID(&it);
                        bool itSelected = ImGui::Selectable(it.m_Name.c_str());

                        ImGui::PopID();
                        selectable |= itSelected;

                        if (itSelected)
                        {
                            memcpy(&inst, &i, sizeof(i));
                        }
                    }
                    ImGui::EndCombo();
                }
                return selectable;
            }

            template <typename EnumType, typename DescType>
            bool ComboInst(const std::string &label, const std::vector<DescType> &descTypes, EnumType &inst, int startIndex = 1, int endIndex = 0)
            {
                ImGui::PushID(&inst);
                bool res = Combo<EnumType, DescType>(label, descTypes, inst, startIndex, endIndex);
                ImGui::PopID();
                return res;
            }

            template<typename EnumType>
            bool ComboEnumMap(const std::string& label, EnumType& e, const std::map<EnumType, std::string> dataMap)
            {
                bool flag{ 0 };
                std::string currentLabel;
                auto it = dataMap.find(e);
                
                if (it != dataMap.end())
                {
                    currentLabel = it->second;
                }
                else
                {
                    currentLabel = "none";
                }

                if (ImGui::BeginCombo(label.c_str(), currentLabel.c_str(), 0))
                {
                    for (auto it : dataMap)
                    {
                        auto itLabel = it.second;
                        ImGui::PushID((int)it.first);
                        if (ImGui::Selectable(itLabel.c_str()))
                        {
                            e = it.first;
                            flag != true;
                        }
                        ImGui::PopID();
                    }
                    ImGui::EndCombo();
                }
                return flag;
            }
            
            static void SetBit(int &value, int off, bool bit)
            {
                if (bit)
                    value |= (1 << off); // Set the bit if the node is opened
                else
                    value &= ~(1 << off); // Clear the bit if the node is closed
            };

            static bool GetBit(int value, int off)
            {
                bool bitMap = (value & (1 << off));
                return bitMap;
            };
            template <typename T>
            bool CheckBoxInst(const std::string &label, T &t)
            {
                ImGui::PushID(&t);
                bool v = (bool)t;
                bool res = ImGui::Checkbox(label.c_str(), &v);
                t = v;
                ImGui::PopID();
                return res;
            }

            void AddFrameBuffer(ImTextureID texId, ImVec2 uv0 = { 0, 1 }, ImVec2 uv1 = { 1, 0 })
            {
                ImDrawList* drawList = ImGui::GetWindowDrawList();
                auto wPos = ImGui::GetWindowPos();
                auto wsz = ImGui::GetWindowSize();
                drawList->AddImage(reinterpret_cast<ImTextureID>(texId), wPos, ImVec2(wPos.x + wsz.x, wPos.y + wsz.y), uv0, uv1);
            }
            void AddFrameBufferLH(ImTextureID texId, ImVec2 uv0 = { 0, 0 }, ImVec2 uv1 = { 1, 1 })
            {
                ImDrawList* drawList = ImGui::GetWindowDrawList();
                auto wPos = ImGui::GetWindowPos();
                auto wsz = ImGui::GetWindowSize();
                drawList->AddImage(reinterpret_cast<ImTextureID>(texId), wPos, ImVec2(wPos.x + wsz.x, wPos.y + wsz.y), uv0, uv1);
            }


            template<typename T>
            bool SelectFromList(const std::string label, std::string& inst, const std::vector<std::string>& ts)
            {
                bool flag = 0;

                if (ImGui::BeginCombo(label.c_str(), inst.c_str()))
                {
                    for (const auto& it : ts)
                    {
                        bool selected = ImGui::Selectable(it.c_str(), it == inst);
                        if (selected)
                        {
                            inst = it;
                        }
                    }
                    ImGui::EndCombo();
                }
                return flag;
            }

            template<typename T>
            bool SelectDataFromList(const std::string& label, T& inst, const std::vector<T>& ts, std::function<bool(const T&, const T&)> func2)
            {
                bool flag = false;

                std::string nameLabel = inst.m_Name;

                if (ImGui::BeginCombo(label.c_str(), nameLabel.c_str()))
                {
                    for (const auto& it : ts)
                    {
                        std::string itLabel = it.m_Name;
                        ImGui::PushID(&it);
                        bool selected = ImGui::Selectable(itLabel.c_str(), func2(inst, it));
                        if (selected)
                        {
                            inst = it;
                            flag = true;
                        }
                        ImGui::PopID();

                    }
                    ImGui::EndCombo();
                }
                return flag;
            }

            template<typename Key, typename T>
            bool SelectFromMap(const std::string& label, Key& inst, const std::map<Key, T>& ts)
            {
                bool flag = false; 

                if (ImGui::BeginCombo(label.c_str(), std::to_string(inst).c_str()))
                {
                    for (const auto& [it, value] : ts)
                    {
                        bool selected = ImGui::Selectable(std::to_string(it).c_str(), it == inst);
                        if (selected)
                        {
                            inst = it;  
                            flag = true;  
                        }
                    }
                    ImGui::EndCombo();
                }

                return flag; 
            }

            bool EditFilePath(const std::string& label, std::string& value);

            template<typename Key, typename T>
            bool ComboMap(const std::string& label, const std::map<Key, std::shared_ptr<T>>& ts, Key& dataIndex, std::shared_ptr<T>& target)
            {

                bool flag{ 0 };

                std::string currentable;
                if (target)
                {
                    currentable = dataIndex.m_Name;
                }

                if (ImGui::BeginCombo(label.c_str(), currentable.c_str()))
                {
                    for (auto [key, value] : ts)
                    {
                        ImGui::PushID(value.get());

                        std::string itLabel = key.m_Name;

                        bool itemFlag = ImGui::Selectable(itLabel.c_str(), dataIndex == key);

                        if (itemFlag)
                        {
                            dataIndex = key;
                            target = value;

                            flag = true;
                        }
                        ImGui::PopID();
                    }
                    ImGui::EndCombo();
                }

                return flag;
            }
        };
    }
}